package com.leetcode.algorithm.y21.m07;

/**
 * https://leetcode-cn.com/study-plan/dynamic-programming/?progress=v43jg2
 * 
 * @author jie.deng
 *
 */
public class DynamicProgramming05 {
	
	/**
	 * 53. 最大子序和 
	 * 给定一个整数数组 nums ，找到一个具有最大和的连续子数组（子数组最少包含一个元素），返回其最大和。
	 * 
	 * 示例:
	 * 输入: [-2,1,-3,4,-1,2,1,-5,4], 
	 * 输出: 6 
	 * 解释: 连续子数组 [4,-1,2,1] 的和最大，为 6。 
	 * 
	 * 进阶:
	 * 如果你已经实现复杂度为 O(n) 的解法，尝试使用更为精妙的分治法求解。
	 * 
	 * @param nums
	 * @return
	 */
	public int maxSubArray(int[] nums) {
		int idx = 0;
		int sum0 = Integer.MIN_VALUE;// 最大和(不包含索引为idx的元素)
		int sum1 = nums[0];// 最大和(包含索引为idx的元素)
		while (++idx < nums.length) {
			sum0 = Math.max(sum0, sum1);
			sum1 = Math.max(sum1 + nums[idx], nums[idx]);
		}
		return Math.max(sum0, sum1);
	}
    
	
	/**
	 * 918. 环形子数组的最大和
	 * 
	 * 给定一个由整数数组 A 表示的环形数组 C，求 C 的非空子数组的最大可能和。
	 * 
	 * 在此处，环形数组意味着数组的末端将会与开头相连呈环状。（形式上，当0 <= i < A.length 时 C[i] = A[i]，且当 i >= 0 时 C[i+A.length] = C[i]）
	 * 
	 * 此外，子数组最多只能包含固定缓冲区 A 中的每个元素一次。（形式上，对于子数组 C[i], C[i+1], ..., C[j]，不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length）
	 * 
	 *  
	 * 
	 * 示例 1：
	 * 
	 * 输入：[1,-2,3,-2]
	 * 输出：3
	 * 解释：从子数组 [3] 得到最大和 3
	 * 示例 2：
	 * 
	 * 输入：[5,-3,5]
	 * 输出：10
	 * 解释：从子数组 [5,5] 得到最大和 5 + 5 = 10
	 * 示例 3：
	 * 
	 * 输入：[3,-1,2,-1]
	 * 输出：4
	 * 解释：从子数组 [2,-1,3] 得到最大和 2 + (-1) + 3 = 4
	 * 示例 4：
	 * 
	 * 输入：[3,-2,2,-3]
	 * 输出：3
	 * 解释：从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
	 * 示例 5：
	 * 
	 * 输入：[-2,-3,-1]
	 * 输出：-1
	 * 解释：从子数组 [-1] 得到最大和 -1
	 *  
	 * 
	 * 提示：
	 * 
	 * -30000 <= A[i] <= 30000
	 * 1 <= A.length <= 30000
	 * 
	 * 来源：力扣（LeetCode）
	 * 链接：https://leetcode-cn.com/problems/maximum-sum-circular-subarray
	 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
	 * 
	 * @param nums
	 * @return
	 */
	public int maxSubarraySumCircular(int[] nums) {
		// 两部分：一部分不包含最后一个元素，一部分包含最后一个元素
		if (nums.length == 1) {
			return nums[0];
		}
		// 不包含最后一个元素，即求 nums[0...len-2]的子数组的最大和
		int idx = 0;
		int sum0 = Integer.MIN_VALUE;// 最大和(不包含索引为idx的元素)，至少包含一个元素
		int sum1 = nums[0];// 最大和(包含索引为idx的元素)
		while (++idx < nums.length - 1) {
			sum0 = Math.max(sum0, sum1);
			sum1 = Math.max(sum1 + nums[idx], nums[idx]);
		}
		int sumMax = Math.max(sum0, sum1);

		// 包含最后一个元素
		// 找到nums子数组的最小和(不包含最后一个元素)
		int sumAll = 0;
		idx = 0;
		for (; idx < nums.length; idx++) {
			sumAll += nums[idx];
		}
		idx = 0;
		int sumMin0 = 0;// 最小和(不包含索引为idx的元素)
		int sumMin1 = nums[0];// 最小和(包含索引为idx的元素)
		while (++idx < nums.length - 1) {
			sumMin0 = Math.min(sumMin0, sumMin1);
			sumMin1 = Math.min(sumMin1 + nums[idx], nums[idx]);
		}
		int sumMaxIncludeLast = sumAll - Math.min(sumMin0, sumMin1);

		return Math.max(sumMax, sumMaxIncludeLast);
	}
    
}
